/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package prjmath;

import java.security.InvalidParameterException;

/**
 *
 * @author antoine.harlin & mehdi.bouazza
 */
public class FPScanner {

    /**
     * Extract the sign of the given bit sequence
     * @param bits the bits sequence
     * @return 1 if the first bits sequence is 0, else return -1
     */
    public static int getSigne(String bits) {

        // détermine le signe
        if (bits.charAt(0) == '0') {
            return 1;
        } else {
            return -1;
        }
    }

    /**
     * Extract the exponent of the given bit sequence
     * @param bits the bits sequence
     * @return the exponent -127
     */
    public static int getFloatExposant(String bits) {
        int exposant = -127;
        // détermine l'exposant
        for (int i = 1; i < 9; i++) {
            if (bits.charAt(i) == '1') {
                exposant += Math.pow(2, 8 - i);
            }
        }
        return exposant;
    }

    /**
     * Extract the fraction of the given bit sequence
     * @param bits the bits sequence
     * @return the fraction
     */
    public static double getFloatMantisse(String bits) {
        double mantisse = 1.0d;
        // détermine la mantisse
        for (int i = 0; i < 23; i++) {
            if (bits.charAt(i + 9) == '1') {
                mantisse += 1 / Math.pow(2, i + 1);
            }
        }
        return mantisse;
    }

    /**
     * Extract the exponent of the given bit sequence
     * @param bits the bits sequence
     * @return the bit value
     */
    public static int getDoubleExposant(String bits) {
        int exposant = -1023;
        // détermine l'exposant
        for (int i = 1; i < 12; i++) {
            if (bits.charAt(i) == '1') {
                exposant += Math.pow(2, 11 - i);
            }
        }
        return exposant;
    }

    /**
     * Extract the fraction of the givent bit sequence
     * @param bits the bits sequence
     * @return the fraction
     */
    public static double getDoubleMantisse(String bits) {
        double mantisse = 1.0d;
        // détermine la mantisse
        for (int i = 0; i < 52; i++) {
            if (bits.charAt(i + 12) == '1') {
                mantisse += 1 / Math.pow(2, i + 1);
            }
        }
        return mantisse;
    }

    /**
     * Converts a 32 characters length String representing bit sequence in a float
     * according to the ANSI/IEEE Std 754-1985 after supressing all spaces in the string
     * @param bits The string representing a bit sequence :
     *  0 00000000 00000000000000000000000 is 0 +
     *  1 00000000 00000000000000000000000 is 0 -
     *  1 00000000 00000000000000000000001 to 0 00000000 111111111111111111111111 are
     *  subnormal numbers from -7.00649232162.10^-46 to +7.00649232162.10^-46
     * @return The string representation of the string bit sequence according to ANSI/IEEE Std 754-1985
     * @throws InvalidParameterException The string sequence bits MUST BE 32 characters long after removing spaces
     */
    public static Float stringBitsToFloat(String bits) throws InvalidParameterException {
        // supression des espaces (plus lisible avant)
        String strtrim = bits.replaceAll(" ", "");
        // Verification de la longueur, doit être 32
        if (strtrim.length() != 32) {
            throw new InvalidParameterException("The length of the string must be 32");
        }

        // récupération du signe, exposant et mantisse
        int signe = getSigne(strtrim);
        int exposant = getFloatExposant(strtrim);
        double mantisse = getFloatMantisse(strtrim);


        // test si infini
        if (exposant == 128) {
            if (mantisse != 1.0f) {
                return Float.NaN;
            }
            if (signe == 1) {
                return Float.POSITIVE_INFINITY;
            } else {
                return Float.NEGATIVE_INFINITY;
            }
        }

        // teste si nombre dénormalisé ou non
        if (exposant != -127) {
            return new Float(mantisse * (Math.pow(2, exposant)) * signe);
        } else {
            exposant = -126;
            mantisse -= 1.0;
            if (mantisse == 0.0) {
                
                if (signe == 1) {
                    return new Float(+0.0f);
                } else {
                    return new Float(-0.0f);
                }
            }
            return new Float(mantisse * (Math.pow(2, exposant)) * signe);
        }
    }

    /**
     * Converts a 64 characters length String representing bit sequence in a double
     * according to the ANSI/IEEE Std 754-1985 after suppressing all spaces in the string
     * @param bits The string representing a bit sequence (0 0000
     * @return the converted Double
     * @throws InvalidParameterException 
     */
    public static Double stringBitsToDouble(String bits) throws InvalidParameterException {
        // supression des espaces (plus lisible avant)
        String strtrim = bits.replaceAll(" ", "");

        // Verification de la longueur, doit être 32
        if (strtrim.length() != 64) {
            throw new InvalidParameterException("The length of the string must be 64");
        }

        // déclaration des variables locales
        int signe = getSigne(strtrim);
        int exposant = getDoubleExposant(strtrim);
        double mantisse = getDoubleMantisse(strtrim);

        // test si infini
        if (exposant == 1024) {
            if (mantisse != 1.0d) {
                return Double.NaN;
            }
            if (signe == 1) {
                return Double.POSITIVE_INFINITY;
            } else {
                return Double.NEGATIVE_INFINITY;
            }
        }

        // teste si nombre dénormalisé ou non
        if (exposant != -1023) {
            return new Double(mantisse * Math.pow(2, exposant) * signe);
        } else {
            exposant = -1022;
            mantisse -= 1.0d;
            if (mantisse == 0.0d) {
                if (signe == 1) {
                    return new Double(+0.0d);
                } else {
                    return new Double(-0.0d);
                }
            }
            return new Double(mantisse * Math.pow(2, exposant) * signe);
        }
    }

    /**
     * Converts a given Float to a String representing its bit sequence using
     * Float.floatToRawIntBits(f)
     * @param f the Float to convert
     * @return a String representing the given Float bit sequence
     */
    public static String floatToStringBits(Float f) {
        int bits = Float.floatToRawIntBits(f);

        StringBuilder s = new StringBuilder();
        for (int i = 0; i < 32; i++) {
            if (bits % 2 == 0) {
                s.insert(0, '0');
            } else {
                s.insert(0, '1');
            }
            bits >>>= 1;
        }

        return s.toString();
    }

    /**
     * Converts a given Double to a String representing its bit sequence using
     * Double.doubleToRawLongBits(f)
     * @param f the Float to convert
     * @return a String representing the given Float bit sequence
     */
    public static String doubleToStringBits(Double d) {
        long bits = Double.doubleToRawLongBits(d);

        StringBuilder s = new StringBuilder();
        for (int i = 0; i < 64; i++) {
            if (bits % 2 == 0) {
                s.insert(0, '0');
            } else {
                s.insert(0, '1');
            }
            bits >>>= 1;
        }
        return s.toString();
    }
}
